Analysis and other experimentation for my Data Driven Shaw Brothers Analysis.

Follow along here!

library(scales)

Attaching package: ‘scales’

The following object is masked from ‘package:purrr’:

    discard

The following objects are masked from ‘package:readr’:

    col_factor, col_numeric
library(ggthemes)
library(tidyjson)
library(tidyverse)
library(forcats)
source("v_theme.R")

Read in Data

First, we need to get the data somehow. Its in JSON format, so let’s read the JSON into a string.

filename = '../out/shaw.json'
shaw_json <- paste(readLines(filename), collapse="")
incomplete final line found on '../out/shaw.json'

Then use tidyjson to convert the nested form into a flat data frame (tibble) we can work with.

films <- shaw_json %>% as.tbl_json %>% gather_array %>%
  spread_values(
    title = jstring("title"),
    director = jstring('director'),
    year = jstring('year'),
    avg_rating = jstring('avg_rating'),
    watches = jstring("watches"),
    likes = jstring("likes")
  )
films %>% head(n = 5) %>% select(title, director, year)

The above table has a row for every film.

We can use tidyjson again to create a row for each actor, duplicating the film-specific data for each actor that had a part in it.

cast <- shaw_json  %>% as.tbl_json  %>% gather_array %>%
  spread_values(
    title = jstring("title"),
    director = jstring('director'),
    year = jstring('year'),
    avg_rating = jstring('avg_rating'),
    watches = jstring("watches"),
    likes = jstring("likes")
  ) %>% enter_object("cast") %>% gather_array() %>%
  spread_values(
    name = jstring("name")
  )
cast %>% head(n = 8) %>% select(title, year, name)

Right now characters are a separte array

characters <- shaw_json  %>% as.tbl_json  %>% gather_array %>%
  spread_values(
    title = jstring("title"),
    director = jstring('director'),
    year = jstring('year'),
    avg_rating = jstring('avg_rating'),
    watches = jstring("watches"),
    likes = jstring("likes")
  ) %>% enter_object("characters") %>% gather_array() %>%
  spread_values(
    name = jstring("name")
  )

Real quick, let’s get a sense of the number of films in our data.

nrow(films)
[1] 260

260! That’s a lot of Kung-Fu. Let’s take a look at these films from a few different angles. We can start with release year.

Shaw Brothers, Through The Ages

So, I said retro, when exactly were these movies made?

source("v_theme.R")
films %>% ggplot(aes(x = year)) +
  geom_bar() +
  labs(title = 'Shaw Bros Films by Year') + 
  #fte_theme()
  theme_fivethirtyeight()
ggsave("imgs/films_by_year.png", width = 8, height = 5)

The first Kung-fu Shaw Brothers film in this data set is Temple of the Red Lotus from 1965. From the reviews, it sounds like it was a bit rough around the edges - but thats about what you would expect from this burgeoning genre.

The studio hits its stride in the early 70’s, with a lull in the mid 70’s and another spike in the late 70’s, early 80’s. Keep in mind that even during the lull, the studio is still putting out 10 or more Kung-fu movies most years.

Shaw Brothers Directorial Favorites

We have the director for each movie in our dataset, let’s look to see if there are any popular standouts.

by_director <- films %>% group_by(director) %>% summarise(n = n()) %>% arrange(-n)
  by_director %>% filter(n > 1) %>%
  ggplot(aes(x = fct_reorder(director, n), y = n)) + 
  geom_bar(stat = "identity") +
  coord_flip() +
  labs(title = 'Counts of Shaw Bros Films by Director') +
  theme_fivethirtyeight()
ggsave("imgs/director_count.png", width = 8, height = 6)

I’d say! Chang Cheh directed 67 or roughly 26% of all Shaw Brothers Kung-fu!

According to his Wikipedia page, he was known as the “The Godfather of Hong Kong cinema”, and rightly so - at least in terms of quantity.

Let’s pull out the top 5 directors, in terms of movie count, and see when they were most active.

# pull out just the top 5 directors
top_directors <- by_director %>% head(n = 5)
# filter films to those directed by these titans of kung-fu
films_top_director <- films %>% filter(director %in% top_directors$director)
#plot bar chart
films_top_director %>%
  ggplot(aes(x = year)) +
  geom_bar(aes(fill = director)) +
  labs(title = 'Shaw Bros Top Director Count by Year') + 
  theme_fivethirtyeight()

# Try Fill Position
films_top_director %>%
  ggplot(aes(x = year)) +
  geom_bar(aes(fill = director), position = "fill") +
  labs(title = 'Shaw Bros Top Director Count by Year') + 
  theme_fivethirtyeight()

That was with filtering to just the top directors. What happens when we put all of them in?

films_top_director_all <- films %>% mutate(director_label = ifelse(director %in% top_directors$director, director, 'Other'))
films_top_director_all %>%
  ggplot(aes(x = year)) +
  geom_bar(aes(fill = director_label)) +
  labs(title = 'Shaw Bros Director Count by Year', fill = '') + 
  theme_fivethirtyeight()
ggsave("imgs/top_directors_by_year.png", width = 8, height = 5)

films_top_director_all %>%
  ggplot(aes(x = year)) +
  geom_bar(aes(fill = director_label), position = 'fill') +
  labs(title = 'Shaw Bros Director Count by Year', fill = '') + 
  theme_fivethirtyeight() +
  scale_y_continuous(labels = percent)
ggsave("imgs/top_directors_by_year_fill.png", width = 8, height = 5)

Title Showdown

library(tidytext)
titles <- films %>% mutate(raw_title = title) %>% unnest_tokens(word, title)
titles_filter <- titles %>% anti_join(stop_words, by = "word")
by_word <- titles_filter %>% count(word, sort = TRUE)
by_word %>%
  filter(n > 3) %>% 
  ggplot(aes(x = fct_reorder(word, n), y = n)) +
  geom_bar(stat = 'identity') +
  coord_flip() + 
  labs(title = 'Top Words Used in Kung-Fu Titles') +
  theme_fivethirtyeight()
ggsave("imgs/top_words_in_titles.png",  width = 8, height = 6)

top_word <- by_word %>% head(n = 12)
films_top_word <- titles %>% filter(word %in% top_word$word)
films_top_word %>% 
  ggplot(aes(x = year)) +
  geom_bar() +
  labs(title = 'Titles with Most Common Words by Year') + 
  facet_wrap( ~ fct_relevel(word, top_word$word)) +
  scale_x_discrete(labels = function(x) { return(ifelse(as.numeric(x) %% 2, x, '')) }) +
  theme_fivethirtyeight() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="none")
ggsave("imgs/top_words_by_time.png", width = 8, height = 6)

Swordsman or Shaolin?

top2_word <- by_word %>% head(n = 2)
films_top2_word <- titles %>% filter(word %in% top2_word$word)
films_top2_word %>% 
  ggplot(aes(x = year, fill = fct_inorder(word))) +
  geom_bar() +
  labs(title = 'Swordsman vs Shaolin by Year', fill = '') + 
  #scale_x_discrete(labels = function(x) { return(ifelse(as.numeric(x) %% 2, x, '')) }) +
  theme_fivethirtyeight() 
  #theme(axis.text.x = element_text(angle = 45, hjust = 1))
ggsave("imgs/shaolin_swordsman.png", width = 8, height = 5)

Swordsman Titles

titles %>% filter(word == 'swordsman') %>% arrange(year) %>% select(raw_title, year)

swordsman success: https://en.wikipedia.org/wiki/One-Armed_Swordsman

It was the first of the new style of wuxia films emphasizing male anti-heroes, violent swordplay and heavy bloodletting. It was the first Hong Kong film to make HK$1 million at the local box office, propelling its star Jimmy Wang to super stardom.

Wuxia Films.

30 Essential Wuxia Films

The Chinese martial arts movie is generally split into two primary subgeneres: the kung fu film and the wuxia film. The kung fu film is newer and focuses primarily on hand-to-hand combat, it’s steeped in traditional fighting forms and there’s a general emphasis on the physical skill of the performer: special effects are generally disdained. Bruce Lee and Jackie Chan are its most famous practitioners and Lau Kar-leung its most important director.

Wuxia is a much older form, based ultimately in the long tradition of Chinese adventure literature, in classic novels such as The Water Margin or Journey to the West, or more contemporary works by authors like Louis Cha and Gu Long. Its heroes follow a very specific code of honor as they navigate the jianghu, an underworld of outlaws and bandits outside the normal streams of civilization.

From Wuxia Women Warriors

Less realistic than its cousin, the kung fu film, wuxia often includes gravity-defying stunts where legendary warriors fly through the air or punch holes straight through their enemies’ chests.

Shaolin Titles

titles %>% filter(word == 'shaolin') %>% arrange(year) %>% select(raw_title, year)

From History in the Shaw Brothers

These films, focused on the Shaolin Temple as a center for anti-Qing resistance, provide a dizzying metaphorical potential, with the Qing variously standing in for Western imperialists, the Japanese, the Nationalist Kuomingtang, the Communists, or even simply the Manchurians themselves, while the Buddhism of the monks allows for examining of the contradictions at the heart of traditional Chinese belief systems, between the imperatives of social justice and withdrawal from worldly concerns.

Actor Troupes and Groups

Even with my novice-level consuption of Shaw Brothers films, one thing I noticed early on was a lot of familiar faces that showed up in many of the movies. My assumption is just like directors, there are a number of actors that are heavily reused in these films. Let’s see if I am right!

First, just like directors, we can look at counts by actor.

by_actor <- cast %>% group_by(name) %>% summarise(n = n()) %>% arrange(-n)
by_actor %>% filter(n > 20) %>%
  ggplot(aes(x = fct_reorder(name, n), y = n)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  labs(title = 'Counts of Shaw Bros Films by Actor') +
  theme_fivethirtyeight()

ggsave("imgs/actor_counts.png", width = 8, height = 6)

Wow! Ku Feng apparently appeared in 82 Kung-Fu movies. That’s a lot of Kung-Fu!

His Wikipedia page isn’t as impressed with this feat as I am, providing little information on this Martial Arts Maniac. Apparently his real name is Chan Sze-man, and his first film was in 1959, and apparently he is still acting. The HKMDB, or Hong Kong Movie Database, provides just a bit more info:

In 1965, Ku formally signed an acting contract with Shaw Brothers where he made around 100 films for them and became most notably known as one of their top character actors. He has worked with just about every top Hong Kong director in a variety of films.

Ok then, well props to you Ku.

Did most of the top actors’ carreers span multiple decades, or did actors come and go quickly? Let’s graph the top actor’s movie count by year.

top_actor <- by_actor %>% head(n = 16)
films_top_actor <- cast %>% filter(name %in% top_actor$name)

films_top_actor %>% 
  ggplot(aes(x = year)) +
  geom_bar() +
  labs(title = 'Top Actors Film Count by Year') + 
  facet_wrap( ~ fct_relevel(name, top_actor$name)) +
  # only label half of the years to make things a bit look cleaner
  scale_x_discrete(labels = function(x) { return(ifelse(as.numeric(x) %% 2, x, '')) }) +
  theme_fivethirtyeight() + 
  # angle label text
  theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="none")
ggsave("imgs/top_actors_by_year.png", width = 6, height = 10)

Finding A Mob of Venoms

Venom Mob

Inspiration from Love Actually Analysis by David Robinson.

Create a matrix of actor co-occurance.

summary(by_actor$n)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   1.000   2.000   4.572   4.000  82.000 
library(reshape2)
# filter actors not in many movies
min_movie_actors <- by_actor %>% filter(n > 5)
popular_cast <- cast %>% filter(name %in% min_movie_actors$name) 
cast_movie_matrix <- popular_cast %>%
  acast(name ~ title,  fun.aggregate = length)
Using name as value column: use value.var to override.
dim(cast_movie_matrix)
[1] 120 258

Rows are actors. Columns are movies.

Filter out movies with few co-occuring actors

cast_movie_df_filtered <- cast_movie_df %>% colSums(.)
norm <- cast_movie_matrix / rowSums(cast_movie_matrix)
hc_norm_cast <- hclust(dist(norm, method = "manhattan"))

Plot:

library(ggdendro)
ggdendrogram(hc_norm_cast, rotate = TRUE)

See ordering:

ordering <-hc_norm_cast$labels[hc_norm_cast$order]
ordering
  [1] "Wong Ching-Ho"         "Kara Hui"              "Gordon Liu Chia-Hui"   "Hsiao Ho"             
  [5] "Wilson Tong"           "Lau Kar-Wing"          "Liu Chia-Liang"        "Chin Ping"            
  [9] "Jimmy Wang Yu"         "Lisa Chiao Chiao"      "Tien Feng"             "Candy Wen Xue-Er"     
 [13] "Lung Tien-Hsiang"      "Philip Kwok Chun-Fung" "Chiang Sheng"          "Lu Feng"              
 [17] "Lo Meng"               "Sun Chien"             "Wong Lik"              "Yu Tai-Ping"          
 [21] "Chin Siu-Ho"           "Lam Chi-Tai"           "Lau Fong-Sai"          "Chiu Hung"            
 [25] "Wong Chung-Shun"       "Tung Li"               "Law Hon"               "Lee Pang-Fei"         
 [29] "Chin Han"              "Wang Ping"             "Ou-Yang Sha-Fei"       "Ivy Ling Po"          
 [33] "Lam Jing"              "Chiang Nan"            "Fung Ngai"             "Fung Hak-On"          
 [37] "Lo Dik"                "Chi Kuan-Chun"         "Leung Kar-Yan"         "Wang Han-Chen"        
 [41] "Choh Seung-Wan"        "Helen Poon Bing-Seung" "Lee Hoi-Sang"          "Phillip Ko Fei"       
 [45] "Wong Yu"               "Chen Kuan-Tai"         "Chiang Tao"            "Chan Sze-Kai"         
 [49] "Yeung Jing-Jing"       "Dang Wai-Ho"           "Jason Pai Piao"        "Kwan Fung"            
 [53] "Ng Hong-Sang"          "Yuen Tak"              "Lo Wei"                "Chiu Sam-Yin"         
 [57] "Hung Lau"              "Walter Tso Tat-Wah"    "Austin Wai"            "Wong Mei-Mei"         
 [61] "Keung Hon"             "Lam Fai-Wong"          "Alexander Fu Sheng"    "Johnny Wang Lung-Wei" 
 [65] "Bruce Tong Yim-Chaan"  "Dick Wei"              "Tang Ching"            "Wong Yung"            
 [69] "Cheng Pei-Pei"         "Lan Wei-Lieh"          "Cheng Lui"             "Wang Kuang-Yu"        
 [73] "Chan Sing"             "Lau Gong"              "Cliff Lok"             "Li Ching"             
 [77] "Shih Szu"              "Wai Wang"              "Chung Wa"              "Teresa Ha Ping"       
 [81] "Bolo Yeung"            "Tin Ching"             "Chen Ping"             "Danny Lee Sau-Yin"    
 [85] "Wong Chung"            "Lau Wing"              "Norman Chu"            "Ku Kuan-Chung"        
 [89] "Ching Li"              "Ngaai Fei"             "Yuen Bun"              "Yuen Wah"             
 [93] "Candice Yu On-On"      "Derek Yee Tung-Sing"   "Lau Wai-Ling"          "Ling Yun"             
 [97] "Tung Lam"              "Fan Mei-Sheng"         "Lily Ho Li-Li"         "Lily Li Li-Li"        
[101] "David Chiang"          "Ti Lung"               "Lee Wan-Chung"         "Chen Hung-Lieh"       
[105] "Fang Mian"             "Shu Pei-Pei"           "Tang Ti"               "Chang Yi"             
[109] "Goo Man-Chung"         "Shum Lo"               "Wu Ma"                 "Cheung Pooi-Saan"     
[113] "Dean Shek"             "Chan Shen"             "Wang Hsieh"            "Ku Feng"              
[117] "Lo Lieh"               "Elliot Ngok"           "Cheng Miu"             "Yeung Chi-Hing"       
ordered_films <- popular_cast %>% arrange(year) %>%
  mutate(film_index = as.numeric(fct_inorder(factor(title))), cast_name = factor(name, levels = ordering))
ordered_films %>% ggplot(aes(film_index, cast_name)) +
    geom_point() +
    geom_path(aes(group = film_index))

# http://stackoverflow.com/questions/13281303/creating-co-occurrence-matrix
cooccur <- cast_movie_matrix %*% t(cast_movie_matrix)
diag(cooccur) <- 0
heatmap(cooccur, symm = TRUE )

cooccur is matrix with rows and columns as actors. The cells for each actor combo indicate the number of movies they have appeared together in.

summary(rowSums(cooccur))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   37.0    63.5    87.5   117.5   143.2   603.0 
summary(colSums(cooccur))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   37.0    63.5    87.5   117.5   143.2   603.0 
summary(colSums(cooccur != 0))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  16.00   34.75   45.00   47.55   54.00  110.00 
collab_counts <- as.data.frame(colSums(cooccur != 0))
library(igraph)
cooccur <- cast_movie_matrix %*% t(cast_movie_matrix)
#cooccur <- ifelse(cooccur < 4, 0, cooccur)
g <- graph.adjacency(cooccur, weighted = TRUE, mode = "undirected", diag = FALSE)
summary(E(g)$weight)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.00    1.00    2.00    2.47    3.00   29.00 
summary(degree(g))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  16.00   34.75   45.00   47.55   54.00  110.00 
summary(strength(g))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   37.0    63.5    87.5   117.5   143.2   603.0 
library(igraph)
cooccur <- cast_movie_matrix %*% t(cast_movie_matrix)
#cooccur <- ifelse(cooccur < 4, 0, cooccur)
g <- graph.adjacency(cooccur, weighted = TRUE, mode = "undirected", diag = FALSE)
low_degree_v <- V(g)[degree(g) < 10] #identify those vertices part of less than three edges
g <- delete_vertices(g, low_degree_v) #exclude them from the graph
low_weight_e <- E(g)[E(g)$weight < 3]
g <- delete_edges(g, low_weight_e)
low_strength_v <- V(g)[strength(g) < 90]
g <- delete_vertices(g, low_strength_v) #exclude them from the graph
V(g)$betweenness <- strength(g)
plot(g, edge.width = E(g)$weight, 
     #layout=layout.fruchterman.reingold,
     layout=layout_with_fr,
     vertex.label.dist=0.5,
     #vertex.size = V(g)$betweenness,
     vertex.size = 3,
     vertex.color='steelblue',
     vertex.frame.color='white',        #the color of the border of the dots 
     vertex.label.color='black',        #the color of the name labels
     vertex.label.font=2,           #the font of the name labels
     vertex.label.cex=1,            #specifies the size of the font of the labels. can also be made to vary
     edge.color = hsv(0,0.2,0.5,alpha=0.2)
)

chiang_sheng <- V(g)[V(g)$name == "Chiang Sheng"]
chiang_sheng_neighbors <- neighbors(g, chiang_sheng)
neighbor_edges <- incident_edges(g, chiang_sheng_neighbors)
sub_g <- make_empty_graph(n = length(chiang_sheng_neighbors), directed = FALSE)
add_vertices(sub_g, chiang_sheng_neighbors)
IGRAPH U--- 12 0 -- 
+ edges:
#add_edges(sub_g, neighbor_edges)
E(g)[neighbor_edges]$color = hsv(0,0.2,0.5,alpha=0.5)
Error in structure(seq_along(x), names = names(x))[i] : 
  invalid subscript type 'list'
plot(g, edge.width = E(g)$weight, 
     #layout=layout.fruchterman.reingold,
     layout=layout_with_fr,
     vertex.label.dist=0.5,
     #vertex.size = V(g)$betweenness,
     vertex.size = 5,
     vertex.color=V(g)$color,
     vertex.frame.color='steelblue',        #the color of the border of the dots 
     vertex.label.color='black',        #the color of the name labels
     vertex.label.font=2,           #the font of the name labels
     vertex.label.cex=1,            #specifies the size of the font of the labels. can also be made to vary
     edge.color = hsv(0,0.2,0.5,alpha=0.2)
)

cooccur_df <- data.frame(cooccur, col.names = colnames(cooccur))
cooccur_df$name <- row.names(cooccur) 
cooccur_df <- cooccur_df %>% select(name, everything())
write_delim(cooccur_df, 'shaw_cooccurance.csv', delim = ';')

Other

R Markdown Notes

This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgU2hhdyBCcm90aGVycyBLdW5nIEZ1IEZpbG1zIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBtYXRoamF4OiBudWxsCiAgICB0aGVtZTogeWV0aQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKQW5hbHlzaXMgYW5kIG90aGVyIGV4cGVyaW1lbnRhdGlvbiBmb3IgbXkgW0RhdGEgRHJpdmVuIFNoYXcgQnJvdGhlcnMgQW5hbHlzaXNdKCkuCgpGb2xsb3cgYWxvbmcgaGVyZSEKCmBgYHtyfQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeSh0aWR5anNvbikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZm9yY2F0cykKCnNvdXJjZSgidl90aGVtZS5SIikKYGBgCgojIyBSZWFkIGluIERhdGEKCkZpcnN0LCB3ZSBuZWVkIHRvIGdldCB0aGUgZGF0YSBzb21laG93LiBJdHMgaW4gSlNPTiBmb3JtYXQsIHNvIGxldCdzIHJlYWQgdGhlIEpTT04gaW50byBhIHN0cmluZy4KCmBgYHtyfQpmaWxlbmFtZSA9ICcuLi9vdXQvc2hhdy5qc29uJwpzaGF3X2pzb24gPC0gcGFzdGUocmVhZExpbmVzKGZpbGVuYW1lKSwgY29sbGFwc2U9IiIpCmBgYAoKVGhlbiB1c2UgW3RpZHlqc29uXShodHRwczovL2dpdGh1Yi5jb20vc2FpbHRocnUvdGlkeWpzb24pIHRvIGNvbnZlcnQgdGhlIG5lc3RlZCBmb3JtIGludG8gYSBmbGF0IGRhdGEgZnJhbWUgKFt0aWJibGVdKGh0dHBzOi8vYmxvZy5yc3R1ZGlvLm9yZy8yMDE2LzAzLzI0L3RpYmJsZS0xLTAtMC8pKSB3ZSBjYW4gd29yayB3aXRoLiAKCmBgYHtyfQpmaWxtcyA8LSBzaGF3X2pzb24gJT4lIGFzLnRibF9qc29uICU+JSBnYXRoZXJfYXJyYXkgJT4lCiAgc3ByZWFkX3ZhbHVlcygKICAgIHRpdGxlID0ganN0cmluZygidGl0bGUiKSwKICAgIGRpcmVjdG9yID0ganN0cmluZygnZGlyZWN0b3InKSwKICAgIHllYXIgPSBqc3RyaW5nKCd5ZWFyJyksCiAgICBhdmdfcmF0aW5nID0ganN0cmluZygnYXZnX3JhdGluZycpLAogICAgd2F0Y2hlcyA9IGpzdHJpbmcoIndhdGNoZXMiKSwKICAgIGxpa2VzID0ganN0cmluZygibGlrZXMiKQogICkKCmZpbG1zICU+JSBoZWFkKG4gPSA1KSAlPiUgc2VsZWN0KHRpdGxlLCBkaXJlY3RvciwgeWVhcikKYGBgCgpUaGUgYWJvdmUgdGFibGUgaGFzIGEgcm93IGZvciBldmVyeSBmaWxtLiAKCldlIGNhbiB1c2UgYHRpZHlqc29uYCBhZ2FpbiB0byBjcmVhdGUgYSByb3cgZm9yIGVhY2ggYWN0b3IsIGR1cGxpY2F0aW5nIHRoZSBmaWxtLXNwZWNpZmljIGRhdGEgZm9yIGVhY2ggYWN0b3IgdGhhdCBoYWQgYSBwYXJ0IGluIGl0LgoKYGBge3J9CmNhc3QgPC0gc2hhd19qc29uICAlPiUgYXMudGJsX2pzb24gICU+JSBnYXRoZXJfYXJyYXkgJT4lCiAgc3ByZWFkX3ZhbHVlcygKICAgIHRpdGxlID0ganN0cmluZygidGl0bGUiKSwKICAgIGRpcmVjdG9yID0ganN0cmluZygnZGlyZWN0b3InKSwKICAgIHllYXIgPSBqc3RyaW5nKCd5ZWFyJyksCiAgICBhdmdfcmF0aW5nID0ganN0cmluZygnYXZnX3JhdGluZycpLAogICAgd2F0Y2hlcyA9IGpzdHJpbmcoIndhdGNoZXMiKSwKICAgIGxpa2VzID0ganN0cmluZygibGlrZXMiKQogICkgJT4lIGVudGVyX29iamVjdCgiY2FzdCIpICU+JSBnYXRoZXJfYXJyYXkoKSAlPiUKICBzcHJlYWRfdmFsdWVzKAogICAgbmFtZSA9IGpzdHJpbmcoIm5hbWUiKQogICkKCmNhc3QgJT4lIGhlYWQobiA9IDgpICU+JSBzZWxlY3QodGl0bGUsIHllYXIsIG5hbWUpCmBgYAoKUmlnaHQgbm93IGNoYXJhY3RlcnMgYXJlIGEgc2VwYXJ0ZSBhcnJheQoKYGBge3J9CmNoYXJhY3RlcnMgPC0gc2hhd19qc29uICAlPiUgYXMudGJsX2pzb24gICU+JSBnYXRoZXJfYXJyYXkgJT4lCiAgc3ByZWFkX3ZhbHVlcygKICAgIHRpdGxlID0ganN0cmluZygidGl0bGUiKSwKICAgIGRpcmVjdG9yID0ganN0cmluZygnZGlyZWN0b3InKSwKICAgIHllYXIgPSBqc3RyaW5nKCd5ZWFyJyksCiAgICBhdmdfcmF0aW5nID0ganN0cmluZygnYXZnX3JhdGluZycpLAogICAgd2F0Y2hlcyA9IGpzdHJpbmcoIndhdGNoZXMiKSwKICAgIGxpa2VzID0ganN0cmluZygibGlrZXMiKQogICkgJT4lIGVudGVyX29iamVjdCgiY2hhcmFjdGVycyIpICU+JSBnYXRoZXJfYXJyYXkoKSAlPiUKICBzcHJlYWRfdmFsdWVzKAogICAgbmFtZSA9IGpzdHJpbmcoIm5hbWUiKQogICkKYGBgCgpSZWFsIHF1aWNrLCBsZXQncyBnZXQgYSBzZW5zZSBvZiB0aGUgbnVtYmVyIG9mIGZpbG1zIGluIG91ciBkYXRhLgoKYGBge3J9Cm5yb3coZmlsbXMpCmBgYAoKMjYwISBUaGF0J3MgYSBsb3Qgb2YgS3VuZy1GdS4gTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlc2UgZmlsbXMgZnJvbSBhIGZldyBkaWZmZXJlbnQgYW5nbGVzLiBXZSBjYW4gc3RhcnQgd2l0aCByZWxlYXNlIHllYXIuCgojIyMgU2hhdyBCcm90aGVycywgVGhyb3VnaCBUaGUgQWdlcwoKU28sIEkgc2FpZCByZXRybywgd2hlbiBleGFjdGx5IHdlcmUgdGhlc2UgbW92aWVzIG1hZGU/CgpgYGB7cn0Kc291cmNlKCJ2X3RoZW1lLlIiKQpmaWxtcyAlPiUgZ2dwbG90KGFlcyh4ID0geWVhcikpICsKICBnZW9tX2JhcigpICsKICBsYWJzKHRpdGxlID0gJ1NoYXcgQnJvcyBGaWxtcyBieSBZZWFyJykgKyAKICAjZnRlX3RoZW1lKCkKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKQpnZ3NhdmUoImltZ3MvZmlsbXNfYnlfeWVhci5wbmciLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKVGhlIGZpcnN0IEt1bmctZnUgU2hhdyBCcm90aGVycyBmaWxtIGluIHRoaXMgZGF0YSBzZXQgaXMgW1RlbXBsZSBvZiB0aGUgUmVkIExvdHVzXShodHRwczovL2xldHRlcmJveGQuY29tL2ZpbG0vdGVtcGxlLW9mLXRoZS1yZWQtbG90dXMvKSBmcm9tIDE5NjUuIEZyb20gdGhlIHJldmlld3MsIGl0IHNvdW5kcyBsaWtlIGl0IHdhcyBhIGJpdCByb3VnaCBhcm91bmQgdGhlIGVkZ2VzIC0gYnV0IHRoYXRzIGFib3V0IHdoYXQgeW91IHdvdWxkIGV4cGVjdCBmcm9tIHRoaXMgYnVyZ2VvbmluZyBnZW5yZS4KClRoZSBzdHVkaW8gaGl0cyBpdHMgc3RyaWRlIGluIHRoZSBlYXJseSA3MCdzLCB3aXRoIGEgbHVsbCBpbiB0aGUgbWlkIDcwJ3MgYW5kIGFub3RoZXIgc3Bpa2UgaW4gdGhlIGxhdGUgNzAncywgZWFybHkgODAncy4gS2VlcCBpbiBtaW5kIHRoYXQgZXZlbiBkdXJpbmcgdGhlIGx1bGwsIHRoZSBzdHVkaW8gaXMgX3N0aWxsXyBwdXR0aW5nIG91dCAxMCBvciBtb3JlIEt1bmctZnUgbW92aWVzIG1vc3QgeWVhcnMuIAoKCiMjIyBTaGF3IEJyb3RoZXJzIERpcmVjdG9yaWFsIEZhdm9yaXRlcwoKV2UgaGF2ZSB0aGUgZGlyZWN0b3IgZm9yIGVhY2ggbW92aWUgaW4gb3VyIGRhdGFzZXQsIGxldCdzIGxvb2sgdG8gc2VlIGlmIHRoZXJlIGFyZSBhbnkgcG9wdWxhciBzdGFuZG91dHMuCgoKYGBge3J9CmJ5X2RpcmVjdG9yIDwtIGZpbG1zICU+JSBncm91cF9ieShkaXJlY3RvcikgJT4lIHN1bW1hcmlzZShuID0gbigpKSAlPiUgYXJyYW5nZSgtbikKCiAgYnlfZGlyZWN0b3IgJT4lIGZpbHRlcihuID4gMSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmN0X3Jlb3JkZXIoZGlyZWN0b3IsIG4pLCB5ID0gbikpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnModGl0bGUgPSAnQ291bnRzIG9mIFNoYXcgQnJvcyBGaWxtcyBieSBEaXJlY3RvcicpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKQpnZ3NhdmUoImltZ3MvZGlyZWN0b3JfY291bnQucG5nIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQpgYGAKCkknZCBzYXkhIFtDaGFuZyBDaGVoXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DaGFuZ19DaGVoKSBkaXJlY3RlZCA2NyBvciByb3VnaGx5IDI2JSBvZiBhbGwgU2hhdyBCcm90aGVycyBLdW5nLWZ1IQoKQWNjb3JkaW5nIHRvIGhpcyBXaWtpcGVkaWEgcGFnZSwgaGUgd2FzIGtub3duIGFzIHRoZSAiVGhlIEdvZGZhdGhlciBvZiBIb25nIEtvbmcgY2luZW1hIiwgYW5kIHJpZ2h0bHkgc28gLSBhdCBsZWFzdCBpbiB0ZXJtcyBvZiBxdWFudGl0eS4gCgpMZXQncyBwdWxsIG91dCB0aGUgdG9wIDUgZGlyZWN0b3JzLCBpbiB0ZXJtcyBvZiBtb3ZpZSBjb3VudCwgYW5kIHNlZSB3aGVuIHRoZXkgd2VyZSBtb3N0IGFjdGl2ZS4gCgpgYGB7cn0KCiMgcHVsbCBvdXQganVzdCB0aGUgdG9wIDUgZGlyZWN0b3JzCnRvcF9kaXJlY3RvcnMgPC0gYnlfZGlyZWN0b3IgJT4lIGhlYWQobiA9IDUpCiMgZmlsdGVyIGZpbG1zIHRvIHRob3NlIGRpcmVjdGVkIGJ5IHRoZXNlIHRpdGFucyBvZiBrdW5nLWZ1CmZpbG1zX3RvcF9kaXJlY3RvciA8LSBmaWxtcyAlPiUgZmlsdGVyKGRpcmVjdG9yICVpbiUgdG9wX2RpcmVjdG9ycyRkaXJlY3RvcikKCiNwbG90IGJhciBjaGFydApmaWxtc190b3BfZGlyZWN0b3IgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhcikpICsKICBnZW9tX2JhcihhZXMoZmlsbCA9IGRpcmVjdG9yKSkgKwogIGxhYnModGl0bGUgPSAnU2hhdyBCcm9zIFRvcCBEaXJlY3RvciBDb3VudCBieSBZZWFyJykgKyAKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKQoKYGBgCgoKYGBge3J9CiMgVHJ5IEZpbGwgUG9zaXRpb24KZmlsbXNfdG9wX2RpcmVjdG9yICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIpKSArCiAgZ2VvbV9iYXIoYWVzKGZpbGwgPSBkaXJlY3RvciksIHBvc2l0aW9uID0gImZpbGwiKSArCiAgbGFicyh0aXRsZSA9ICdTaGF3IEJyb3MgVG9wIERpcmVjdG9yIENvdW50IGJ5IFllYXInKSArIAogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpCmBgYAoKVGhhdCB3YXMgd2l0aCBmaWx0ZXJpbmcgdG8gX2p1c3RfIHRoZSB0b3AgZGlyZWN0b3JzLiBXaGF0IGhhcHBlbnMgd2hlbiB3ZSBwdXQgYWxsIG9mIHRoZW0gaW4/CgpgYGB7cn0KZmlsbXNfdG9wX2RpcmVjdG9yX2FsbCA8LSBmaWxtcyAlPiUgbXV0YXRlKGRpcmVjdG9yX2xhYmVsID0gaWZlbHNlKGRpcmVjdG9yICVpbiUgdG9wX2RpcmVjdG9ycyRkaXJlY3RvciwgZGlyZWN0b3IsICdPdGhlcicpKQoKZmlsbXNfdG9wX2RpcmVjdG9yX2FsbCAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsID0gZGlyZWN0b3JfbGFiZWwpKSArCiAgbGFicyh0aXRsZSA9ICdTaGF3IEJyb3MgRGlyZWN0b3IgQ291bnQgYnkgWWVhcicsIGZpbGwgPSAnJykgKyAKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKQoKZ2dzYXZlKCJpbWdzL3RvcF9kaXJlY3RvcnNfYnlfeWVhci5wbmciLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKYGBge3J9CmZpbG1zX3RvcF9kaXJlY3Rvcl9hbGwgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhcikpICsKICBnZW9tX2JhcihhZXMoZmlsbCA9IGRpcmVjdG9yX2xhYmVsKSwgcG9zaXRpb24gPSAnZmlsbCcpICsKICBsYWJzKHRpdGxlID0gJ1NoYXcgQnJvcyBEaXJlY3RvciBDb3VudCBieSBZZWFyJywgZmlsbCA9ICcnKSArIAogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudCkKZ2dzYXZlKCJpbWdzL3RvcF9kaXJlY3RvcnNfYnlfeWVhcl9maWxsLnBuZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSkKYGBgCgoKCiMjIFRpdGxlIFNob3dkb3duCgpgYGB7cn0KbGlicmFyeSh0aWR5dGV4dCkKCiMgbG9hZCBzdG9wX3dvcmRzIGludG8gUiBlbnZpcm9ubWVudApkYXRhKCJzdG9wX3dvcmRzIikKYGBgCgoKYGBge3J9CiMgc2F2ZXMgZW50aXJlIHRpdGxlIGluIGB0aXRsZV9hbGxgIGNvbHVtbiwgCiMgdGhlbiBzcGxpdHMgdXAgdGl0bGUgY29sdW1uIGNyZWF0aW5nIHRoZSBgd29yZGAgY29sdW1uIC0gCiMgd2l0aCBhIHJvdyBmb3IgZXZlcnkgdG9rZW4gKHdvcmQpLgp0aXRsZXMgPC0gZmlsbXMgJT4lIG11dGF0ZSh0aXRsZV9hbGwgPSB0aXRsZSkgJT4lIHVubmVzdF90b2tlbnMod29yZCwgdGl0bGUpCmBgYAoKYGBge3J9CiMgZmlsdGVyIHN0b3B3b3Jkcwp0aXRsZXNfZmlsdGVyIDwtIHRpdGxlcyAlPiUgYW50aV9qb2luKHN0b3Bfd29yZHMsIGJ5ID0gIndvcmQiKQpgYGAKCgpgYGB7cn0KYnlfd29yZCA8LSB0aXRsZXNfZmlsdGVyICU+JSBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkKYnlfd29yZCAlPiUKICBmaWx0ZXIobiA+IDMpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBmY3RfcmVvcmRlcih3b3JkLCBuKSwgeSA9IG4pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBjb29yZF9mbGlwKCkgKyAKICBsYWJzKHRpdGxlID0gJ1RvcCBXb3JkcyBVc2VkIGluIEt1bmctRnUgVGl0bGVzJykgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpCmdnc2F2ZSgiaW1ncy90b3Bfd29yZHNfaW5fdGl0bGVzLnBuZyIsICB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCmBgYAoKCmBgYHtyfQp0b3Bfd29yZCA8LSBieV93b3JkICU+JSBoZWFkKG4gPSAxMikKZmlsbXNfdG9wX3dvcmQgPC0gdGl0bGVzICU+JSBmaWx0ZXIod29yZCAlaW4lIHRvcF93b3JkJHdvcmQpCgpmaWxtc190b3Bfd29yZCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0geWVhcikpICsKICBnZW9tX2JhcigpICsKICBsYWJzKHRpdGxlID0gJ1RpdGxlcyB3aXRoIE1vc3QgQ29tbW9uIFdvcmRzIGJ5IFllYXInKSArIAogIGZhY2V0X3dyYXAoIH4gZmN0X3JlbGV2ZWwod29yZCwgdG9wX3dvcmQkd29yZCkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHsgcmV0dXJuKGlmZWxzZShhcy5udW1lcmljKHgpICUlIDIsIHgsICcnKSkgfSkgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgbGVnZW5kLnBvc2l0aW9uPSJub25lIikKZ2dzYXZlKCJpbWdzL3RvcF93b3Jkc19ieV90aW1lLnBuZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKCmBgYAoKU3dvcmRzbWFuIG9yIFNoYW9saW4/CgpgYGB7cn0KdG9wMl93b3JkIDwtIGJ5X3dvcmQgJT4lIGhlYWQobiA9IDIpCmZpbG1zX3RvcDJfd29yZCA8LSB0aXRsZXMgJT4lIGZpbHRlcih3b3JkICVpbiUgdG9wMl93b3JkJHdvcmQpCgpmaWxtc190b3AyX3dvcmQgJT4lIAogIGdncGxvdChhZXMoeCA9IHllYXIsIGZpbGwgPSBmY3RfaW5vcmRlcih3b3JkKSkpICsKICBnZW9tX2JhcigpICsKICBsYWJzKHRpdGxlID0gJ1N3b3Jkc21hbiB2cyBTaGFvbGluIGJ5IFllYXInLCBmaWxsID0gJycpICsgCiAgI3NjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgeyByZXR1cm4oaWZlbHNlKGFzLm51bWVyaWMoeCkgJSUgMiwgeCwgJycpKSB9KSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgCiAgI3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpCmdnc2F2ZSgiaW1ncy9zaGFvbGluX3N3b3Jkc21hbi5wbmciLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUpCmBgYAoKKipTd29yZHNtYW4gVGl0bGVzKioKCmBgYHtyfQp0aXRsZXMgJT4lIGZpbHRlcih3b3JkID09ICdzd29yZHNtYW4nKSAlPiUgYXJyYW5nZSh5ZWFyKSAlPiUgc2VsZWN0KHRpdGxlX2FsbCwgeWVhcikKYGBgCgpzd29yZHNtYW4gc3VjY2VzczogaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvT25lLUFybWVkX1N3b3Jkc21hbgoKPiAgSXQgd2FzIHRoZSBmaXJzdCBvZiB0aGUgbmV3IHN0eWxlIG9mIHd1eGlhIGZpbG1zIGVtcGhhc2l6aW5nIG1hbGUgYW50aS1oZXJvZXMsIHZpb2xlbnQgc3dvcmRwbGF5IGFuZCBoZWF2eSBibG9vZGxldHRpbmcuIEl0IHdhcyB0aGUgZmlyc3QgSG9uZyBLb25nIGZpbG0gdG8gbWFrZSBISyQxIG1pbGxpb24gYXQgdGhlIGxvY2FsIGJveCBvZmZpY2UsIHByb3BlbGxpbmcgaXRzIHN0YXIgSmltbXkgV2FuZyB0byBzdXBlciBzdGFyZG9tLgoKV3V4aWEgRmlsbXMuIAoKWzMwIEVzc2VudGlhbCBXdXhpYSBGaWxtc10oaHR0cHM6Ly90aGVlbmRvZmNpbmVtYS5uZXQvMjAxNi8wMi8xMS8zMC1lc3NlbnRpYWwtd3V4aWEtZmlsbXMvKQoKPiBUaGUgQ2hpbmVzZSBtYXJ0aWFsIGFydHMgbW92aWUgaXMgZ2VuZXJhbGx5IHNwbGl0IGludG8gdHdvIHByaW1hcnkgc3ViZ2VuZXJlczogdGhlIGt1bmcgZnUgZmlsbSBhbmQgdGhlIHd1eGlhIGZpbG0uIFRoZSBrdW5nIGZ1IGZpbG0gaXMgbmV3ZXIgYW5kIGZvY3VzZXMgcHJpbWFyaWx5IG9uIGhhbmQtdG8taGFuZCBjb21iYXQsIGl04oCZcyBzdGVlcGVkIGluIHRyYWRpdGlvbmFsIGZpZ2h0aW5nIGZvcm1zIGFuZCB0aGVyZeKAmXMgYSBnZW5lcmFsIGVtcGhhc2lzIG9uIHRoZSBwaHlzaWNhbCBza2lsbCBvZiB0aGUgcGVyZm9ybWVyOiBzcGVjaWFsIGVmZmVjdHMgYXJlIGdlbmVyYWxseSBkaXNkYWluZWQuIEJydWNlIExlZSBhbmQgSmFja2llIENoYW4gYXJlIGl0cyBtb3N0IGZhbW91cyBwcmFjdGl0aW9uZXJzIGFuZCBMYXUgS2FyLWxldW5nIGl0cyBtb3N0IGltcG9ydGFudCBkaXJlY3Rvci4KCj4gV3V4aWEgaXMgYSBtdWNoIG9sZGVyIGZvcm0sIGJhc2VkIHVsdGltYXRlbHkgaW4gdGhlIGxvbmcgdHJhZGl0aW9uIG9mIENoaW5lc2UgYWR2ZW50dXJlIGxpdGVyYXR1cmUsIGluIGNsYXNzaWMgbm92ZWxzIHN1Y2ggYXMgVGhlIFdhdGVyIE1hcmdpbiBvciBKb3VybmV5IHRvIHRoZSBXZXN0LCBvciBtb3JlIGNvbnRlbXBvcmFyeSB3b3JrcyBieSBhdXRob3JzIGxpa2UgTG91aXMgQ2hhIGFuZCBHdSBMb25nLiBJdHMgaGVyb2VzIGZvbGxvdyBhIHZlcnkgc3BlY2lmaWMgY29kZSBvZiBob25vciBhcyB0aGV5IG5hdmlnYXRlIHRoZSBqaWFuZ2h1LCBhbiB1bmRlcndvcmxkIG9mIG91dGxhd3MgYW5kIGJhbmRpdHMgb3V0c2lkZSB0aGUgbm9ybWFsIHN0cmVhbXMgb2YgY2l2aWxpemF0aW9uLgoKCkZyb20gW1d1eGlhIFdvbWVuIFdhcnJpb3JzXShodHRwOi8vd3d3LmluZGlld2lyZS5jb20vMjAxNS8xMC84LWV4dHJhb3JkaW5hcnktd3V4aWEtZmlsbXMtcG93ZXJlZC1ieS13YXJyaW9yLXdvbWVuLTU2NTI3LykKCj4gTGVzcyByZWFsaXN0aWMgdGhhbiBpdHMgY291c2luLCB0aGUga3VuZyBmdSBmaWxtLCB3dXhpYSBvZnRlbiBpbmNsdWRlcyBncmF2aXR5LWRlZnlpbmcgc3R1bnRzIHdoZXJlIGxlZ2VuZGFyeSB3YXJyaW9ycyBmbHkgdGhyb3VnaCB0aGUgYWlyIG9yIHB1bmNoIGhvbGVzIHN0cmFpZ2h0IHRocm91Z2ggdGhlaXIgZW5lbWllc+KAmSBjaGVzdHMuCgoqKlNoYW9saW4gVGl0bGVzKioKCmBgYHtyfQp0aXRsZXMgJT4lIGZpbHRlcih3b3JkID09ICdzaGFvbGluJykgJT4lIGFycmFuZ2UoeWVhcikgJT4lIHNlbGVjdCh0aXRsZV9hbGwsIHllYXIpCmBgYAoKCkZyb20gW0hpc3RvcnkgaW4gdGhlIFNoYXcgQnJvdGhlcnNdKGh0dHA6Ly90aGV2dWxnYXJjaW5lbWEuY29tLzIwMTUvMTIvaGlzdG9yeS1pbi10aGUtc2hhdy1icm90aGVycy8pCgo+IFRoZXNlIGZpbG1zLCBmb2N1c2VkIG9uIHRoZSBTaGFvbGluIFRlbXBsZSBhcyBhIGNlbnRlciBmb3IgYW50aS1RaW5nIHJlc2lzdGFuY2UsIHByb3ZpZGUgYSBkaXp6eWluZyBtZXRhcGhvcmljYWwgcG90ZW50aWFsLCB3aXRoIHRoZSBRaW5nIHZhcmlvdXNseSBzdGFuZGluZyBpbiBmb3IgV2VzdGVybiBpbXBlcmlhbGlzdHMsIHRoZSBKYXBhbmVzZSwgdGhlIE5hdGlvbmFsaXN0IEt1b21pbmd0YW5nLCB0aGUgQ29tbXVuaXN0cywgb3IgZXZlbiBzaW1wbHkgdGhlIE1hbmNodXJpYW5zIHRoZW1zZWx2ZXMsIHdoaWxlIHRoZSBCdWRkaGlzbSBvZiB0aGUgbW9ua3MgYWxsb3dzIGZvciBleGFtaW5pbmcgb2YgdGhlIGNvbnRyYWRpY3Rpb25zIGF0IHRoZSBoZWFydCBvZiB0cmFkaXRpb25hbCBDaGluZXNlIGJlbGllZiBzeXN0ZW1zLCBiZXR3ZWVuIHRoZSBpbXBlcmF0aXZlcyBvZiBzb2NpYWwganVzdGljZSBhbmQgd2l0aGRyYXdhbCBmcm9tIHdvcmxkbHkgY29uY2VybnMuCgojIyBBY3RvciBUcm91cGVzIGFuZCBHcm91cHMKCkV2ZW4gd2l0aCBteSBub3ZpY2UtbGV2ZWwgY29uc3VwdGlvbiBvZiBTaGF3IEJyb3RoZXJzIGZpbG1zLCBvbmUgdGhpbmcgSSBub3RpY2VkIGVhcmx5IG9uIHdhcyBhIGxvdCBvZiBmYW1pbGlhciBmYWNlcyB0aGF0IHNob3dlZCB1cCBpbiBtYW55IG9mIHRoZSBtb3ZpZXMuIE15IGFzc3VtcHRpb24gaXMganVzdCBsaWtlIGRpcmVjdG9ycywgdGhlcmUgYXJlIGEgbnVtYmVyIG9mIGFjdG9ycyB0aGF0IGFyZSBoZWF2aWx5IHJldXNlZCBpbiB0aGVzZSBmaWxtcy4gTGV0J3Mgc2VlIGlmIEkgYW0gcmlnaHQhCgpGaXJzdCwganVzdCBsaWtlIGRpcmVjdG9ycywgd2UgY2FuIGxvb2sgYXQgY291bnRzIGJ5IGFjdG9yLiAKCmBgYHtyIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA0fQpieV9hY3RvciA8LSBjYXN0ICU+JSBncm91cF9ieShuYW1lKSAlPiUgc3VtbWFyaXNlKG4gPSBuKCkpICU+JSBhcnJhbmdlKC1uKQoKYnlfYWN0b3IgJT4lIGZpbHRlcihuID4gMjApICU+JQogIGdncGxvdChhZXMoeCA9IGZjdF9yZW9yZGVyKG5hbWUsIG4pLCB5ID0gbikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh0aXRsZSA9ICdDb3VudHMgb2YgU2hhdyBCcm9zIEZpbG1zIGJ5IEFjdG9yJykgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpCmdnc2F2ZSgiaW1ncy9hY3Rvcl9jb3VudHMucG5nIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQpgYGAKCldvdyEgW0t1IEZlbmddKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0t1X0ZlbmcpIGFwcGFyZW50bHkgYXBwZWFyZWQgaW4gODIgS3VuZy1GdSBtb3ZpZXMuIFRoYXQncyBhIGxvdCBvZiBLdW5nLUZ1IQoKSGlzIFdpa2lwZWRpYSBwYWdlIGlzbid0IGFzIGltcHJlc3NlZCB3aXRoIHRoaXMgZmVhdCBhcyBJIGFtLCBwcm92aWRpbmcgbGl0dGxlIGluZm9ybWF0aW9uIG9uIHRoaXMgTWFydGlhbCBBcnRzIE1hbmlhYy4gQXBwYXJlbnRseSBoaXMgcmVhbCBuYW1lIGlzIENoYW4gU3plLW1hbiwgYW5kIGhpcyBmaXJzdCBmaWxtIHdhcyBpbiAxOTU5LCBhbmQgYXBwYXJlbnRseSBoZSBpcyBzdGlsbCBhY3RpbmcuIFRoZSBbSEtNREJdKGh0dHA6Ly9oa21kYi5jb20vZGIvcGVvcGxlL3ZpZXcubWh0bWw/aWQ9MzU3OSZkaXNwbGF5X3NldD1lbmcpLCBvciBIb25nIEtvbmcgTW92aWUgRGF0YWJhc2UsIHByb3ZpZGVzIGp1c3QgYSBiaXQgbW9yZSBpbmZvOgoKPiBJbiAxOTY1LCBLdSBmb3JtYWxseSBzaWduZWQgYW4gYWN0aW5nIGNvbnRyYWN0IHdpdGggU2hhdyBCcm90aGVycyB3aGVyZSBoZSBtYWRlIGFyb3VuZCAxMDAgZmlsbXMgZm9yIHRoZW0gYW5kIGJlY2FtZSBtb3N0IG5vdGFibHkga25vd24gYXMgb25lIG9mIHRoZWlyIHRvcCBjaGFyYWN0ZXIgYWN0b3JzLiBIZSBoYXMgd29ya2VkIHdpdGgganVzdCBhYm91dCBldmVyeSB0b3AgSG9uZyBLb25nIGRpcmVjdG9yIGluIGEgdmFyaWV0eSBvZiBmaWxtcy4KCk9rIHRoZW4sIHdlbGwgcHJvcHMgdG8geW91IEt1LgoKRGlkIG1vc3Qgb2YgdGhlIHRvcCBhY3RvcnMnIGNhcnJlZXJzIHNwYW4gbXVsdGlwbGUgZGVjYWRlcywgb3IgZGlkIGFjdG9ycyBjb21lIGFuZCBnbyBxdWlja2x5PyBMZXQncyBncmFwaCB0aGUgdG9wIGFjdG9yJ3MgbW92aWUgY291bnQgYnkgeWVhci4gCgpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD01fQp0b3BfYWN0b3IgPC0gYnlfYWN0b3IgJT4lIGhlYWQobiA9IDE2KQpmaWxtc190b3BfYWN0b3IgPC0gY2FzdCAlPiUgZmlsdGVyKG5hbWUgJWluJSB0b3BfYWN0b3IkbmFtZSkKCmZpbG1zX3RvcF9hY3RvciAlPiUgCiAgZ2dwbG90KGFlcyh4ID0geWVhcikpICsKICBnZW9tX2JhcigpICsKICBsYWJzKHRpdGxlID0gJ1RvcCBBY3RvcnMgRmlsbSBDb3VudCBieSBZZWFyJykgKyAKICBmYWNldF93cmFwKCB+IGZjdF9yZWxldmVsKG5hbWUsIHRvcF9hY3RvciRuYW1lKSkgKwogICMgb25seSBsYWJlbCBoYWxmIG9mIHRoZSB5ZWFycyB0byBtYWtlIHRoaW5ncyBhIGJpdCBsb29rIGNsZWFuZXIKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHsgcmV0dXJuKGlmZWxzZShhcy5udW1lcmljKHgpICUlIDIsIHgsICcnKSkgfSkgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsgCiAgIyBhbmdsZSBsYWJlbCB0ZXh0CiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwgbGVnZW5kLnBvc2l0aW9uPSJub25lIikKZ2dzYXZlKCJpbWdzL3RvcF9hY3RvcnNfYnlfeWVhci5wbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDEwKQpgYGAKCmBgYHtyfQoKYGBgCgoKIyMgRmluZGluZyBBIE1vYiBvZiBWZW5vbXMgCgpbVmVub20gTW9iXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9WZW5vbV9Nb2IpCgpJbnNwaXJhdGlvbiBmcm9tIFtMb3ZlIEFjdHVhbGx5IEFuYWx5c2lzXShodHRwOi8vdmFyaWFuY2VleHBsYWluZWQub3JnL3IvbG92ZS1hY3R1YWxseS1uZXR3b3JrLykgYnkgRGF2aWQgUm9iaW5zb24uCgpDcmVhdGUgYSBtYXRyaXggb2YgYWN0b3IgY28tb2NjdXJhbmNlLgoKYGBge3J9CnN1bW1hcnkoYnlfYWN0b3IkbikKYGBgCgoKYGBge3J9CmxpYnJhcnkocmVzaGFwZTIpCgojIGZpbHRlciBhY3RvcnMgbm90IGluIG1hbnkgbW92aWVzCm1pbl9tb3ZpZV9hY3RvcnMgPC0gYnlfYWN0b3IgJT4lIGZpbHRlcihuID4gNSkKcG9wdWxhcl9jYXN0IDwtIGNhc3QgJT4lIGZpbHRlcihuYW1lICVpbiUgbWluX21vdmllX2FjdG9ycyRuYW1lKSAKCmNhc3RfbW92aWVfbWF0cml4IDwtIHBvcHVsYXJfY2FzdCAlPiUKICBhY2FzdChuYW1lIH4gdGl0bGUsICBmdW4uYWdncmVnYXRlID0gbGVuZ3RoKQpkaW0oY2FzdF9tb3ZpZV9tYXRyaXgpCmBgYAoKUm93cyBhcmUgYWN0b3JzLiBDb2x1bW5zIGFyZSBtb3ZpZXMuIAoKCkZpbHRlciBvdXQgbW92aWVzIHdpdGggZmV3IGNvLW9jY3VyaW5nIGFjdG9ycwoKYGBge3J9CmNhc3RfbW92aWVfZGZfZmlsdGVyZWQgPC0gY2FzdF9tb3ZpZV9kZiAlPiUgY29sU3VtcyguKQpgYGAKCgpgYGB7cn0Kbm9ybSA8LSBjYXN0X21vdmllX21hdHJpeCAvIHJvd1N1bXMoY2FzdF9tb3ZpZV9tYXRyaXgpCgpoY19ub3JtX2Nhc3QgPC0gaGNsdXN0KGRpc3Qobm9ybSwgbWV0aG9kID0gIm1hbmhhdHRhbiIpKQoKYGBgCgoKUGxvdDoKCmBgYHtyLCBmaWcuaGVpZ2h0PTQuOCwgZmlnLndpZHRoPTMuNn0KbGlicmFyeShnZ2RlbmRybykKCmdnZGVuZHJvZ3JhbShoY19ub3JtX2Nhc3QsIHJvdGF0ZSA9IFRSVUUpCmBgYAoKU2VlIG9yZGVyaW5nOgoKYGBge3J9Cm9yZGVyaW5nIDwtaGNfbm9ybV9jYXN0JGxhYmVsc1toY19ub3JtX2Nhc3Qkb3JkZXJdCm9yZGVyaW5nCmBgYAoKYGBge3J9Cm9yZGVyZWRfZmlsbXMgPC0gcG9wdWxhcl9jYXN0ICU+JSBhcnJhbmdlKHllYXIpICU+JQogIG11dGF0ZShmaWxtX2luZGV4ID0gYXMubnVtZXJpYyhmY3RfaW5vcmRlcihmYWN0b3IodGl0bGUpKSksIGNhc3RfbmFtZSA9IGZhY3RvcihuYW1lLCBsZXZlbHMgPSBvcmRlcmluZykpCgpvcmRlcmVkX2ZpbG1zICU+JSBnZ3Bsb3QoYWVzKGZpbG1faW5kZXgsIGNhc3RfbmFtZSkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX3BhdGgoYWVzKGdyb3VwID0gZmlsbV9pbmRleCkpCmBgYAoKCmBgYHtyfQojIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTMyODEzMDMvY3JlYXRpbmctY28tb2NjdXJyZW5jZS1tYXRyaXgKY29vY2N1ciA8LSBjYXN0X21vdmllX21hdHJpeCAlKiUgdChjYXN0X21vdmllX21hdHJpeCkKCmRpYWcoY29vY2N1cikgPC0gMAoKaGVhdG1hcChjb29jY3VyLCBzeW1tID0gVFJVRSApCmBgYAoKY29vY2N1ciBpcyBtYXRyaXggd2l0aCByb3dzIGFuZCBjb2x1bW5zIGFzIGFjdG9ycy4gVGhlIGNlbGxzIGZvciBlYWNoIGFjdG9yIGNvbWJvIGluZGljYXRlIHRoZSBudW1iZXIgb2YgbW92aWVzIHRoZXkgaGF2ZSBhcHBlYXJlZCB0b2dldGhlciBpbi4KCmBgYHtyfQpzdW1tYXJ5KHJvd1N1bXMoY29vY2N1cikpCgpzdW1tYXJ5KGNvbFN1bXMoY29vY2N1cikpCgpzdW1tYXJ5KGNvbFN1bXMoY29vY2N1ciAhPSAwKSkKCmNvbGxhYl9jb3VudHMgPC0gYXMuZGF0YS5mcmFtZShjb2xTdW1zKGNvb2NjdXIgIT0gMCkpCmBgYAoKYGBge3J9CmxpYnJhcnkoaWdyYXBoKQoKY29vY2N1ciA8LSBjYXN0X21vdmllX21hdHJpeCAlKiUgdChjYXN0X21vdmllX21hdHJpeCkKI2Nvb2NjdXIgPC0gaWZlbHNlKGNvb2NjdXIgPCA0LCAwLCBjb29jY3VyKQoKZyA8LSBncmFwaC5hZGphY2VuY3koY29vY2N1ciwgd2VpZ2h0ZWQgPSBUUlVFLCBtb2RlID0gInVuZGlyZWN0ZWQiLCBkaWFnID0gRkFMU0UpCgpzdW1tYXJ5KEUoZykkd2VpZ2h0KQoKc3VtbWFyeShkZWdyZWUoZykpCgpzdW1tYXJ5KHN0cmVuZ3RoKGcpKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KbGlicmFyeShpZ3JhcGgpCgpjb29jY3VyIDwtIGNhc3RfbW92aWVfbWF0cml4ICUqJSB0KGNhc3RfbW92aWVfbWF0cml4KQojY29vY2N1ciA8LSBpZmVsc2UoY29vY2N1ciA8IDQsIDAsIGNvb2NjdXIpCgpnIDwtIGdyYXBoLmFkamFjZW5jeShjb29jY3VyLCB3ZWlnaHRlZCA9IFRSVUUsIG1vZGUgPSAidW5kaXJlY3RlZCIsIGRpYWcgPSBGQUxTRSkKCmxvd19kZWdyZWVfdiA8LSBWKGcpW2RlZ3JlZShnKSA8IDEwXSAjaWRlbnRpZnkgdGhvc2UgdmVydGljZXMgcGFydCBvZiBsZXNzIHRoYW4gdGhyZWUgZWRnZXMKZyA8LSBkZWxldGVfdmVydGljZXMoZywgbG93X2RlZ3JlZV92KSAjZXhjbHVkZSB0aGVtIGZyb20gdGhlIGdyYXBoCgpsb3dfd2VpZ2h0X2UgPC0gRShnKVtFKGcpJHdlaWdodCA8IDNdCmcgPC0gZGVsZXRlX2VkZ2VzKGcsIGxvd193ZWlnaHRfZSkKCmxvd19zdHJlbmd0aF92IDwtIFYoZylbc3RyZW5ndGgoZykgPCA5MF0KZyA8LSBkZWxldGVfdmVydGljZXMoZywgbG93X3N0cmVuZ3RoX3YpICNleGNsdWRlIHRoZW0gZnJvbSB0aGUgZ3JhcGgKClYoZykkYmV0d2Vlbm5lc3MgPC0gc3RyZW5ndGgoZykKCnBsb3QoZywgZWRnZS53aWR0aCA9IEUoZykkd2VpZ2h0LCAKICAgICAjbGF5b3V0PWxheW91dC5mcnVjaHRlcm1hbi5yZWluZ29sZCwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZnIsCiAgICAgdmVydGV4LmxhYmVsLmRpc3Q9MC41LAogICAgICN2ZXJ0ZXguc2l6ZSA9IFYoZykkYmV0d2Vlbm5lc3MsCiAgICAgdmVydGV4LnNpemUgPSAzLAogICAgIHZlcnRleC5jb2xvcj0nc3RlZWxibHVlJywKICAgICB2ZXJ0ZXguZnJhbWUuY29sb3I9J3doaXRlJywgCQkjdGhlIGNvbG9yIG9mIHRoZSBib3JkZXIgb2YgdGhlIGRvdHMgCiAgICAgdmVydGV4LmxhYmVsLmNvbG9yPSdibGFjaycsCQkjdGhlIGNvbG9yIG9mIHRoZSBuYW1lIGxhYmVscwogICAgIHZlcnRleC5sYWJlbC5mb250PTIsCQkJI3RoZSBmb250IG9mIHRoZSBuYW1lIGxhYmVscwogICAgIHZlcnRleC5sYWJlbC5jZXg9MSwJCQkjc3BlY2lmaWVzIHRoZSBzaXplIG9mIHRoZSBmb250IG9mIHRoZSBsYWJlbHMuIGNhbiBhbHNvIGJlIG1hZGUgdG8gdmFyeQogICAgIGVkZ2UuY29sb3IgPSBoc3YoMCwwLjIsMC41LGFscGhhPTAuMikKCikKYGBgCgoKYGBge3J9CmNoaWFuZ19zaGVuZyA8LSBWKGcpW1YoZykkbmFtZSA9PSAiQ2hpYW5nIFNoZW5nIl0KCmNoaWFuZ19zaGVuZ19uZWlnaGJvcnMgPC0gbmVpZ2hib3JzKGcsIGNoaWFuZ19zaGVuZykKbmVpZ2hib3JfZWRnZXMgPC0gaW5jaWRlbnRfZWRnZXMoZywgY2hpYW5nX3NoZW5nX25laWdoYm9ycykKCnN1Yl9nIDwtIG1ha2VfZW1wdHlfZ3JhcGgobiA9IGxlbmd0aChjaGlhbmdfc2hlbmdfbmVpZ2hib3JzKSwgZGlyZWN0ZWQgPSBGQUxTRSkKYWRkX3ZlcnRpY2VzKHN1Yl9nLCBjaGlhbmdfc2hlbmdfbmVpZ2hib3JzKQoKI2FkZF9lZGdlcyhzdWJfZywgbmVpZ2hib3JfZWRnZXMpCiNFKGcpW25laWdoYm9yX2VkZ2VzXSRjb2xvciA9IGhzdigwLDAuMiwwLjUsYWxwaGE9MC41KQpWKGcpJGNvbG9yID0gImdyZXkiClYoZylbY2hpYW5nX3NoZW5nXSRjb2xvciA9ICJ0b21hdG8iClYoZylbY2hpYW5nX3NoZW5nX25laWdoYm9yc10kY29sb3IgPSAndG9tYXRvJwpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpwbG90KGcsIGVkZ2Uud2lkdGggPSBFKGcpJHdlaWdodCwgCiAgICAgI2xheW91dD1sYXlvdXQuZnJ1Y2h0ZXJtYW4ucmVpbmdvbGQsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2ZyLAogICAgIHZlcnRleC5sYWJlbC5kaXN0PTAuNSwKICAgICAjdmVydGV4LnNpemUgPSBWKGcpJGJldHdlZW5uZXNzLAogICAgIHZlcnRleC5zaXplID0gNSwKICAgICB2ZXJ0ZXguY29sb3I9VihnKSRjb2xvciwKICAgICB2ZXJ0ZXguZnJhbWUuY29sb3I9J3N0ZWVsYmx1ZScsIAkJI3RoZSBjb2xvciBvZiB0aGUgYm9yZGVyIG9mIHRoZSBkb3RzIAogICAgIHZlcnRleC5sYWJlbC5jb2xvcj0nYmxhY2snLAkJI3RoZSBjb2xvciBvZiB0aGUgbmFtZSBsYWJlbHMKICAgICB2ZXJ0ZXgubGFiZWwuZm9udD0yLAkJCSN0aGUgZm9udCBvZiB0aGUgbmFtZSBsYWJlbHMKICAgICB2ZXJ0ZXgubGFiZWwuY2V4PTEsCQkJI3NwZWNpZmllcyB0aGUgc2l6ZSBvZiB0aGUgZm9udCBvZiB0aGUgbGFiZWxzLiBjYW4gYWxzbyBiZSBtYWRlIHRvIHZhcnkKICAgICBlZGdlLmNvbG9yID0gaHN2KDAsMC4yLDAuNSxhbHBoYT0wLjIpCikKYGBgCgoKYGBge3J9CmNvb2NjdXJfZGYgPC0gZGF0YS5mcmFtZShjb29jY3VyLCBjb2wubmFtZXMgPSBjb2xuYW1lcyhjb29jY3VyKSkKY29vY2N1cl9kZiRuYW1lIDwtIHJvdy5uYW1lcyhjb29jY3VyKSAKY29vY2N1cl9kZiA8LSBjb29jY3VyX2RmICU+JSBzZWxlY3QobmFtZSwgZXZlcnl0aGluZygpKQp3cml0ZV9kZWxpbShjb29jY3VyX2RmLCAnc2hhd19jb29jY3VyYW5jZS5jc3YnLCBkZWxpbSA9ICc7JykKYGBgCgpgYGB7cn0KCmBgYAoKCgojIyBPdGhlcgoKIyMgUiBNYXJrZG93biBOb3RlcwoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4KCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ21kK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCg==